/**
 * 持续不动笔墨不读书
 * 活用内存映射与虚拟内存，让应用更高效
 * 极大提高执行效率 增强应用的安全性
 * 内存映射
 * 内存保护
 * 内存锁定
 * 
 * 内存映射 内存映射的功能 进程的内存映射表中建立一条新的映射表项
 * 也就是分配一块新的虚拟内存给调用进程
 * 提供内存映射功能的系统调用是
 * void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
 * 参数addr指定期望的映射目标地址，如果给addr传递NULL，则表示让内核选择一个合适的地址，一般都是NULL
 * length指的是映射的数据的字节数，内核会把它对齐到内存页大小的下一个倍数
 * 参数prot指定地址映射之后的访问权限，取值可以是PROT_NONE 或者是PROT_READ,PROT_WRITE,PROT_EXEC
 * fd指定要映射的文件
 * offset指定映射内通在文件中的偏移量
 * flags指定执行内存映射时使用的操作选项，不同选项的组合适用于不同的应用场景
 * MAP_PRIVATE私有映射。变化不更新到磁盘文件中
 * MAP_SHARED 共享映射 更新到磁盘文件中 更新后，其中任意一个进程对内存内容的修改都会立即被其他进程可见
 * MAP_ANONYMOUS 创建匿名映射、MAP_FIXED使用指定的固定地址执行映射
 * 
 * 优化下载服务器上的文件IO
 * mmap最常见的应用就是优化文件的IO，它可以把一个文件的内容映射到虚拟内存中，使得后续所有对文件IO操作都转换为内存地址的读写
 * 
 * 内核会使用内核缓冲区来提高读写效率，所以在使用read write系统调用读写文件时，
 * 数据其实经过两次拷贝，一次是在存储设备与内核缓冲区之间
 * 另一次内核缓冲区与应用层之间
 * 
 * 使用mmap映射文件内容时，没有创建内核缓冲区，而是直接在虚拟内存地址与文件之间建立关联
 * 文件内容直接传输到应用层的内存分页上，减少了一次内存拷贝，因为提高了效率
 * 
 * 通常使用文件内容来初始化内存数据，通常并不需要修改映射文件的内容，直接使用私有映射(MAP_PRIVATE)标志就可以了
 * 
 * 优化内容频繁变化的文件IO
 * 内存中变化的内容，需要反应到硬盘文件上进行永久性的保存
 * 文件的大小并不会发生变化，发生变化的只是文件的内容 例如各种虚拟机软件的虚拟硬盘文件
 * 在这种模式下，就可以使用共享映射MAP_SHARED标志 内容同步更新到磁盘文件上，从而既可以简化虚拟机逻辑，又能提高文件IO效率
 * 
 * 大块内存分配
 * glibc 内存分配函数malloc的内部，会使用mmap函数代替堆内存边界调整函数brk
 * 
 * 好处 映射解除后，应用占用的虚拟内存总量就会降下来
 * brk调整边界的方式，当释放的内存不在堆内存边界的时候，堆内存的最高水位线是降不下来的
 * 
 * mmap缺点就是系统开销比较大，因为需要经过内核，内核先要查找合适的映射地址，还会把分配的内存内容都置为零
 * 再返回给应用层
 * 
 * glibc设计中，只有生命周期比较长的大块内存才适合使用mmap进行分配
 * 
 * 32MB 128KB
 * 
 * 多进程内存共享
 * MAP_SHARED选项 关联到相同的磁盘文件，就可以创建在多个进程间共享的内存块
 * mmap shared 会将内存中的更新保存到磁盘=文件上，所以可以提供数据的持久化保存功能
 * 父子进程之间共享内存块，而不需要持久化功能，还可以使用共享的匿名映射（MAP_SHARED | MAP_ANONYMOUS）
 * 与使用共享内存的方式相比，以这样的方式创建的共享内存块，不会显示在IPC的输出列表里
 * 而且可以把共享范围严格限制在父子进程之间，因而更加适合有私密性要求的数据的共享
 * 
 * 将多个文件的内容映射到连续的内存
 * flags还有一个应用比较多的选项MAP_FIXED，表示把内存映射到某个指定的固定位置
 * 使用这个选项，就可以把分布在多个文件，或者单个文件不同位置处的内容，映射到连续的内存地址处
 * 使后续的处理逻辑得以简化
 * 
 * MAP_SHARED与MAP_FIXED组合使用，实现内存中更新的内容自动保存到相应文件的功能
 * 匿名的私有映射配合使用 
 * 
 * ***/
void * addr = mmap(NULL, length, prot_flag, MAP_PRIVATE | MAP_ANONYMOUS, 0);
/**
 * addr为基地址
 * 内存映射功能强大 能同时简化应用逻辑和提高IO性能
 * 映射后的内存地址可以通过mremap系统调用修改，如果有对映射内存内部数据的地址引用，需要用相对映射首地址的偏移量的形式来保存
 * 否则，在映射地址修改之后，引用会出错
 * 
 * 内存保护
 * 内存保护系统调用的佛你功能是修改一块虚拟内存区域上的访问权限 比如增加可执行权限内，或者取消可修改权限等
 * 
 * **/
int mprotect(void *addr,size_t length,int prot);
/***
 * addr指定要修改的虚拟内存的首地址，必须是内存页大小的整数倍
 * length指定要修改的虚拟内存区的长度，内核会把它向上舍入内存分页大喜爱的下一个整数倍
 * prot就是执行内存区域上的新的访问权限，可以是PROT_NONE或者PROT_READ PROT_WRITE PROT_EXEC 组合
 * 
 * 内存锁定
 * 使得指定区域的内存始终保持在物理内存中，而不会被交换到交换分区上
 * 对锁定后的内存的访问，不会因为出发缺页中断而发生访问延迟，因此，把访问频率非常高的内存数据锁进内存，能提高程序的性能
 * 
 * 锁定指定内存段
 * 
*/
int mlock(void *addr,size_t length);
/**
 * 解锁
 * **/
int munlock(void *addr,size_t length);
/**
 * 解锁后的内存页不会立即从物理内存中释放，而只是标记后续可以被换出到交换分区
 * 具体被换出的时机，要取决于系统的负载，以及内核采用的换入换出策略
 * 锁定进程的所有内存页
 * 提供了两个接口，控制整个进程的内存锁定行为
 * 
 * **/
int mlockall(int flags);
int munlockall(void);
/**
 * 分别对调用进程的所有内存页执行锁定或解锁操作
 * 可提高后续内存的使用效率
 * 
 * 如果尽早地将文件的数据存入内存，不从磁盘重新读取，IO的性能还是蛮高的
 * 要是能进行内存锁定，那就是固定了内存的分区，对固定的内存进行操作还是很快的
 * 要是每次从磁盘读取，进程不断滴进行新的内存分配，还是蛮慢的
 * 内存还有权限的保护
 * **/